<!--
 * @Author: Lw
 * @Date: 2022-09-01 15:41:49
 * @LastEditors: LHL
 * @LastEditTime: 2022-10-18 16:52:48
-->
<template>
  <n-space vertical size="large">
    <n-space justify="space-between">
      <n-space>
        <n-button
          v-for="(item, index) in leftBtns"
          :key="index"
          size="large"
          :type="item.type"
          :disabled="item.disabled"
          :secondary="item.secondary"
          @click="(e) => handleBtnClick(e, item)"
        >
          {{ item.label }}
        </n-button>
        <slot name="headerLeft"></slot>
      </n-space>
      <n-space>
        <n-button
          v-for="(item, index) in rightBtns"
          :key="index"
          size="large"
          :type="item.type"
          :disabled="item.disabled"
          :secondary="item.secondary || true"
          @click="(e) => handleBtnClick(e, item)"
        >
          {{ item.label }}
        </n-button>
        <slot name="headerRight"></slot>
      </n-space>
    </n-space>
    <n-spin :show="loading">
      <n-data-table
        v-bind="props"
        ref="tableRef"
        :columns="columns"
        :data="data"
        :row-key="(row) => row.id"
        :pagination="pagination"
        :remote="true"
        :cascade="config.cascade"
        :scroll-x="config.scrollX"
        :style="{ height: `${height}px` }"
        @load="onLoad"
        flex-height
        v-model:checked-row-keys="checkedRowKeys"
        :render-cell="renderCell"
        :allow-checking-not-loaded="true"
        @update:sorter="updateSorter"
      />
    </n-spin>
  </n-space>
  <n-popconfirm
    :show="showPopover"
    :x="popoverX"
    :y="popoverY"
    @positive-click="handlePositiveClick"
    @negative-click="handleNegativeClick"
  >
    是否继续操作？
  </n-popconfirm>
</template>

<script setup>
  import { NSpace, NButton, NDropdown } from 'naive-ui'
  import { getViewportOffset } from '@/utils/domUtils'
  import { useWindowSize } from '@/utils/useWindowSize'
  import { h } from 'vue'
  import { tableProps } from './props'
  const emit = defineEmits(['click', 'checked', 'pageChange', 'onLoad', 'sorter'])
  const props = defineProps({
    ...tableProps,
  })
  const tableRef = ref()
  /** 表格高度 */
  const height = ref(600)
  /** 选中项 */
  const checkedRowKeys = ref([])
  /** 分页配置 */
  const pagination = props.config?.page
    ? reactive({
        // 当前页码
        page: props.config?.page ?? 1,
        // 总条数
        itemCount: props.config?.total ?? 0,
        // 每页条数
        pageSize: props.config?.pageSize ?? 10,
        pageSizes: [10, 20, 50],
        showSizePicker: true,
        prefix: (res) => `共 ${res.itemCount} 条`,
        onChange: (page) => {
          pagination.page = page
          emit('pageChange', {
            page: pagination.page,
            pageSize: pagination.pageSize,
          })
        },
        onUpdatePageSize: (pageSize) => {
          pagination.pageSize = pageSize
          pagination.page = 1
          emit('pageChange', {
            page: pagination.page,
            pageSize: pagination.pageSize,
          })
        },
      })
    : undefined
  /** 多选配置 */
  const isSelection = props.config?.selection
  /** 列头配置 */
  const columns = computed(() => {
    const res = props.config.columns
    // 多选框列
    if (isSelection && res?.[0]?.type != 'selection') {
      res.unshift({
        type: 'selection',
        fixed: 'left',
        disabled: props.config?.selectionDisabled,
      })
    }
    // 操作列按钮
    if (props.config?.btns?.filter((item) => ['row-right', 'row-right-more'].includes(item.position))?.length > 0) {
      const operateCol = operateBtns(toRaw(props.config.btns))
      // console.log(operateCol)
      if (res?.[res.length - 1]?.key === 'operate') {
        // 覆盖操作列
        res[res.length - 1] = operateCol
      } else {
        // 添加操作列
        res.push(operateCol)
      }
    }

    return res
  })
  /** 左上角按钮数组 */
  const leftBtns = computed(() => {
    if (props.config.btns?.length > 0) {
      return props.config.btns.filter((item) => item.position === 'left')
    } else {
      return []
    }
  })
  /** 右上角按钮数组 */
  const rightBtns = computed(() => {
    if (props.config.btns?.length > 0) {
      return props.config.btns.filter((item) => item.position === 'right')
    } else {
      return []
    }
  })
  /** 操作列构建 */
  const operateBtns = (data) => {
    const rightBtns = data.filter((item) => item.position === 'row-right')
    const moreBtns = data.filter((item) => item.position === 'row-right-more')
    const res = {
      title: '操作',
      key: 'operate',
      width: props.config?.operateWidth || 150,
      fixed: 'right',
      render(row) {
        return h(
          NSpace,
          {
            size: 12,
          },
          {
            default: () => {
              let btns = []
              if (rightBtns.length > 0) {
                // 常规操作按钮
                btns = [
                  ...btns,
                  rightBtns.map((item) => {
                    return h(
                      NButton,
                      {
                        type: item.type,
                        text: true,
                        size: 'mini',
                        disabled:
                          typeof item.disabled !== 'undefined'
                            ? typeof item.disabled === 'boolean'
                              ? item.disabled
                              : item.disabled(row)
                            : false,
                        onClick: (e) => handleBtnClick(e, item, row),
                      },
                      { default: () => item.label },
                    )
                  }),
                ]
              }
              if (moreBtns.length > 0) {
                // 更多操作按钮
                btns.push(
                  h(
                    NDropdown,
                    {
                      size: 'large',
                      placement: 'bottom-center',
                      trigger: 'click',
                      options: moreBtns.map((item) => {
                        return {
                          label: item.label,
                          key: item.value,
                          disabled:
                            typeof item.disabled !== 'undefined'
                              ? typeof item.disabled === 'boolean'
                                ? item.disabled
                                : item.disabled(row)
                              : false,
                        }
                      }),
                      onSelect: (key) =>
                        handleBtnClick(
                          null,
                          {
                            value: key,
                            position: 'row-right',
                          },
                          row,
                        ),
                    },
                    {
                      default: () =>
                        h(
                          NButton,
                          {
                            type: 'info',
                            text: true,
                            size: 'mini',
                            'icon-placement': 'right',
                          },
                          { default: () => '更多' },
                        ),
                    },
                  ),
                )
              }
              return btns
            },
          },
        )
      },
    }
    return res
  }

  const showPopover = ref(false)
  const popoverX = ref(0)
  const popoverY = ref(0)
  let popoverKey = null
  let popoverData = null
  /** 按钮点击事件 */
  const handleBtnClick = (e, btn, row) => {
    const { label, value, position } = btn
    let data
    switch (position) {
      case 'left':
        if (isSelection) {
          data = toRaw(props.data).filter((item) => checkedRowKeys.value.includes(item.id))
        } else {
          data = null
        }
        break
      case 'right':
        if (isSelection) {
          data = toRaw(props.data).filter((item) => checkedRowKeys.value.includes(item.id))
        } else {
          data = null
        }
        break
      case 'row-right':
        data = toRaw(row)
        break
    }
    // 弹出确认
    if (label?.includes('删除')) {
      popoverX.value = e.clientX
      popoverY.value = e.clientY
      showPopover.value = true
      popoverKey = value
      popoverData = data
      return
    }
    emit('click', {
      key: value,
      data,
    })
  }
  /** 选中事件 */
  const handleChecked = () => {
    emit('checked', checkedRowKeys.value)
  }
  /** 弹出确认-确认 */
  const handlePositiveClick = () => {
    showPopover.value = false
    emit('click', {
      key: popoverKey,
      data: popoverData,
    })
  }
  /** 弹出确认-取消 */
  const handleNegativeClick = () => {
    showPopover.value = false
  }
  /** 异步展开树形事件 */
  const onLoad = (row) => {
    return new Promise((resolve) => {
      setTimeout(() => {
        emit('onLoad', row)
        resolve()
      }, 1000)
    })
  }
  /** 表格自适应相关 */
  const computeTableHeight = () => {
    if (!props.config.height || props.config.height === 'auto') {
      const table = unref(tableRef)
      if (!table) return
      const tableEl = table?.$el
      const headEl = tableEl.querySelector('.n-data-table-thead ')
      const { bottomIncludeBody } = getViewportOffset(headEl)
      if (bottomIncludeBody - 24 - 36 > (props.config?.pagination ? 240 + 40 : 240)) {
        height.value = bottomIncludeBody - 24 - 36
      } else {
        height.value = 600
      }
    } else if (props.config?.height) {
      height.value = props.config.height
    }
  }

  watch(checkedRowKeys, () => {
    handleChecked()
  })
  watch(
    () => props.data,
    (data) => {
      if (props.isCheckedRowKeys) {
        checkedRowKeys.value = []
      }
    },
  )
  watch(
    () => props.config.page,
    (page) => {
      pagination.page = page
    },
  )
  watch(
    () => props.config.total,
    (total) => {
      pagination.itemCount = total
    },
  )
  onMounted(() => {
    nextTick(() => {
      computeTableHeight()
      useWindowSize(computeTableHeight, 300)
    })
  })

  function updateSorter(options) {
    emit('sorter', options)
  }

  defineExpose({
    checkedRowKeys,
  })
</script>

<style lang="scss" scoped>
  :deep.n-data-table {
    .n-data-table-td {
      border-bottom: 1px solid var(--n-merged-border-color);
    }
  }
</style>
